library(tidyverse)
library(GGally)
library(ggfortify)

1.

Load the housing_prices.csv data set and undertake an initial exploration of the data.

housing <- read_csv("data/housing.csv")
Rows: 20640 Columns: 10── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): ocean_proximity
dbl (9): longitude, latitude, housing_median_age, total_rooms, total_bedrooms, population, households, me...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
housing_prices <- read_csv("data/housing_prices.csv")
Rows: 19675 Columns: 10── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): ocean_proximity
dbl (9): longitude, latitude, housing_median_age, total_rooms, total_bedrooms, population, households, me...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
glimpse(housing)
Rows: 20,640
Columns: 10
$ longitude          <dbl> -122.23, -122.22, -122.24, -122.25, -122.25, -122.25, -122.25, -122.25, -122.26,…
$ latitude           <dbl> 37.88, 37.86, 37.85, 37.85, 37.85, 37.85, 37.84, 37.84, 37.84, 37.84, 37.85, 37.…
$ housing_median_age <dbl> 41, 21, 52, 52, 52, 52, 52, 52, 42, 52, 52, 52, 52, 52, 52, 50, 52, 52, 50, 52, …
$ total_rooms        <dbl> 880, 7099, 1467, 1274, 1627, 919, 2535, 3104, 2555, 3549, 2202, 3503, 2491, 696,…
$ total_bedrooms     <dbl> 129, 1106, 190, 235, 280, 213, 489, 687, 665, 707, 434, 752, 474, 191, 626, 283,…
$ population         <dbl> 322, 2401, 496, 558, 565, 413, 1094, 1157, 1206, 1551, 910, 1504, 1098, 345, 121…
$ households         <dbl> 126, 1138, 177, 219, 259, 193, 514, 647, 595, 714, 402, 734, 468, 174, 620, 264,…
$ median_income      <dbl> 8.3252, 8.3014, 7.2574, 5.6431, 3.8462, 4.0368, 3.6591, 3.1200, 2.0804, 3.6912, …
$ median_house_value <dbl> 452600, 358500, 352100, 341300, 342200, 269700, 299200, 241400, 226700, 261100, …
$ ocean_proximity    <chr> "NEAR BAY", "NEAR BAY", "NEAR BAY", "NEAR BAY", "NEAR BAY", "NEAR BAY", "NEAR BA…
summary(housing)
   longitude         latitude     housing_median_age  total_rooms    total_bedrooms     population   
 Min.   :-124.3   Min.   :32.54   Min.   : 1.00      Min.   :    2   Min.   :   1.0   Min.   :    3  
 1st Qu.:-121.8   1st Qu.:33.93   1st Qu.:18.00      1st Qu.: 1448   1st Qu.: 296.0   1st Qu.:  787  
 Median :-118.5   Median :34.26   Median :29.00      Median : 2127   Median : 435.0   Median : 1166  
 Mean   :-119.6   Mean   :35.63   Mean   :28.64      Mean   : 2636   Mean   : 537.9   Mean   : 1425  
 3rd Qu.:-118.0   3rd Qu.:37.71   3rd Qu.:37.00      3rd Qu.: 3148   3rd Qu.: 647.0   3rd Qu.: 1725  
 Max.   :-114.3   Max.   :41.95   Max.   :52.00      Max.   :39320   Max.   :6445.0   Max.   :35682  
                                                                     NA's   :207                     
   households     median_income     median_house_value ocean_proximity   
 Min.   :   1.0   Min.   : 0.4999   Min.   : 14999     Length:20640      
 1st Qu.: 280.0   1st Qu.: 2.5634   1st Qu.:119600     Class :character  
 Median : 409.0   Median : 3.5348   Median :179700     Mode  :character  
 Mean   : 499.5   Mean   : 3.8707   Mean   :206856                       
 3rd Qu.: 605.0   3rd Qu.: 4.7432   3rd Qu.:264725                       
 Max.   :6082.0   Max.   :15.0001   Max.   :500001                       
                                                                         

2.

We expect the total_rooms of houses to be strongly correlated with total_bedrooms. Use ggpairs() to investigate correlations between these two variables.

housing %>% 
  select(total_rooms, total_bedrooms) %>% 
  ggpairs(progress = FALSE)

3.

So, we do find significant correlations. Let’s drop total_bedrooms from the dataset, and use only total_rooms going forward.

housing_trim <- housing %>% 
  select(-total_bedrooms)

housing_prices_trim <- housing_prices %>% 
  select(-total_bedrooms)

Added At Review ———————————————————–

We could engineer some new variables.

housing_trim_eng <- housing_trim %>%
  mutate(rooms_per_house = total_rooms / households,
         people_per_house = population / households,
         rooms_per_person = total_rooms / population)

housing_trim_eng

housing_prices_eng <- housing_prices_trim %>%
  mutate(rooms_per_house = total_rooms / households,
         people_per_house = population / households,
         rooms_per_person = total_rooms / population)

housing_prices_eng
NA
NA

And take a look at these using skim() - which when we do, we can see that a lot of the variables are positively skewed.

Positively skewed data is not good for linear models.


housing_trim_eng %>% 
  skimr::skim() %>% 
  view()

Where we have skewed data that is non-negative, we can log transform it to make it more normal.


housing_prices_eng %>%
  ggplot(aes(x = median_income)) +
  geom_histogram()


housing_prices_eng %>%
  ggplot(aes(x = median_house_value)) +
  geom_histogram()

housing_log <- housing_prices_eng %>% 
  select(-c(ocean_proximity, latitude, longitude, housing_median_age)) %>% 
  mutate(across(everything(), log)) %>% 
  rename_with(~ paste0("log_", .x)) %>% 
  bind_cols(housing_prices_eng)
housing_log %>%
  ggplot(aes(x = log_median_income)) +
  geom_histogram()


housing_log %>%
  ggplot(aes(x = log_median_house_value)) +
  geom_histogram()

With values now transformed and more normal. Lets have a look for some relationships.

housing_log %>% 
  ggplot(aes(x = median_income, y = median_house_value)) +
  geom_point()


housing_log %>% 
  ggplot(aes(x = ocean_proximity, y = median_house_value)) +
  geom_boxplot()

There appears to be some kind of relationship between ocean proximity and house value.

housing_log %>% 
  ggplot(aes(x = longitude, y = latitude, colour = ocean_proximity)) +
  geom_point()

Lets group some of those levels!

housing_ocean <- housing_log %>% 
  mutate(ocean_prox_grouped = if_else(
    ocean_proximity %in% c("<1H OCEAN", "NEAR BAY", "NEAR OCEAN"), "NEAR WATER", ocean_proximity))

housing_ocean
housing_ocean %>% 
  ggplot(aes(x = longitude, y = latitude, colour = ocean_prox_grouped)) +
  geom_point()


housing_ocean %>%
  select(log_median_house_value, 
         log_total_rooms, 
         log_population, 
         log_households,
         log_rooms_per_house,
         log_people_per_house,
         log_rooms_per_person) %>% 
  ggpairs(progress = FALSE)

housing_ocean %>%
  select(log_median_house_value, housing_median_age, log_median_income, ocean_prox_grouped) %>% 
  ggpairs(aes(colour = ocean_prox_grouped, 
              alpha = 0.5),
          progress = FALSE)

model1 <- lm(log_median_house_value ~ log_median_income,
             data = housing_ocean)

autoplot(model1)

summary(model1)

Call:
lm(formula = log_median_house_value ~ log_median_income, data = housing_ocean)

Residuals:
     Min       1Q   Median       3Q      Max 
-2.59210 -0.26868  0.00162  0.25743  2.22388 

Coefficients:
                   Estimate Std. Error t value Pr(>|t|)    
(Intercept)       11.094953   0.008498  1305.6   <2e-16 ***
log_median_income  0.776362   0.006600   117.6   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4086 on 19673 degrees of freedom
Multiple R-squared:  0.4129,    Adjusted R-squared:  0.4129 
F-statistic: 1.384e+04 on 1 and 19673 DF,  p-value: < 2.2e-16

Once have a Simple Linear Regression we want to identify which predictors to add next.

We do this by considering our residuals.

library(modelr)
housing_ocean %>% 
  add_residuals(model = model1) %>%  # Adds redis column, difference from actual to fitted median_house_value 
  select(log_median_house_value, 
         ocean_prox_grouped, 
         log_rooms_per_person, 
         log_people_per_house, 
         resid) %>% 
  ggpairs(progress = FALSE)

model1 <- lm(log_median_house_value ~ log_median_income,
             data = housing_ocean)

autoplot(model1)

4.1

We are interested in developing a regression model for the median_house_value of a house in terms of the possible predictor variables in the dataset.

Use ggpairs() to investigate correlations between median_house_value and the predictors (this may take a while to run, don’t worry, make coffee or something).

ggpairs(housing_trim, progress = FALSE)

4.2

Perform further ggplot visualisations of any significant correlations you find.

ggpairs() shows a strong correlation between median_house_value and median_income, with a Correlation Coefficient of 0.688.

This is visualised below.


housing_trim %>% 
  ggplot(aes(x = median_income, y = median_house_value)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE)

5.

Shortly we may try a regression model to fit the categorical predictor ocean_proximity. Investigate the level of ocean_proximity predictors. How many dummy variables do you expect to get from it?

There are 5 levels of ocean_proximity so we would use 4 dummies.


housing_trim %>% 
  distinct(ocean_proximity)

6.

Start with simple linear regression. Regress median_house_value on median_income and check the regression diagnostics.

housing_model <- lm(median_house_value ~ median_income, 
                    data = housing_trim)
autoplot(housing_model)

Plot 1: I’m unsure of the correct interpretation of this; however, the smoothed line suggests no clear pattern to distribution staying relatively flat and close to 0. To the my eye though it looks as though lower fitted values are over-estimated and higher fitted values are under-estimated. Plot 2: There is some variation from the normal distribution, although the residuals are relatively normally distributed. Plot 3: I’m unsure on the correct interpretation of this; however, the smoothed line stays relatively constant/flat although to my eye there looks to be funneling at the higher end of the fitted values.

7

Add another predictor of your choice. Check your assumptions, diagnostics, and interpret the model.

housing_trim %>% 
  ggplot(aes(x = median_house_value, y = ocean_proximity))+
  geom_boxplot()

housing_model_2 <- lm(median_house_value ~ median_income + ocean_proximity, 
                    data = housing_trim)
autoplot(housing_model_2)

My interpretations here are the same as the first model, although again I’m a little unsure if these are correct.

Plot 1: I’m unsure of the correct interpretation of this; however, the smoothed line suggests no clear pattern to distribution staying relatively flat and close to 0. To the my eye though it looks as though lower fitted values are over-estimated and higher fitted values are under-estimated. Plot 2: There is some variation from the normal distribution, although the residuals are relatively normally distributed. Plot 3: I’m unsure on the correct interpretation of this; however, the smoothed line stays relatively constant/flat although to my eye there looks to be funneling at the higher end of the fitted values.

summary(housing_model_2)

Call:
lm(formula = median_house_value ~ median_income + ocean_proximity, 
    data = housing_trim)

Residuals:
    Min      1Q  Median      3Q     Max 
-507454  -46266  -12876   28563  475483 

Coefficients:
                          Estimate Std. Error t value Pr(>|t|)    
(Intercept)                83470.0     1414.3  59.019  < 2e-16 ***
median_income              37018.7      279.6 132.384  < 2e-16 ***
ocean_proximityINLAND     -77457.4     1232.8 -62.829  < 2e-16 ***
ocean_proximityISLAND     195375.2    33139.8   5.895 3.79e-09 ***
ocean_proximityNEAR BAY    21267.6     1731.2  12.285  < 2e-16 ***
ocean_proximityNEAR OCEAN  17675.1     1633.7  10.819  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 74080 on 20634 degrees of freedom
Multiple R-squared:  0.588, Adjusted R-squared:  0.5879 
F-statistic:  5890 on 5 and 20634 DF,  p-value: < 2.2e-16
LS0tCnRpdGxlOiAiV2VlayAxMCAvIERheSAyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KGdnZm9ydGlmeSkKCmBgYAoKIyAxLgpMb2FkIHRoZSBob3VzaW5nX3ByaWNlcy5jc3YgZGF0YSBzZXQgYW5kIHVuZGVydGFrZSBhbiBpbml0aWFsIGV4cGxvcmF0aW9uIG9mIHRoZSBkYXRhLgoKYGBge3J9CmhvdXNpbmcgPC0gcmVhZF9jc3YoImRhdGEvaG91c2luZy5jc3YiKQoKaG91c2luZ19wcmljZXMgPC0gcmVhZF9jc3YoImRhdGEvaG91c2luZ19wcmljZXMuY3N2IikKYGBgCgpgYGB7cn0KZ2xpbXBzZShob3VzaW5nKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KGhvdXNpbmcpCmBgYAoKIyAyLiAKV2UgZXhwZWN0IHRoZSB0b3RhbF9yb29tcyBvZiBob3VzZXMgdG8gYmUgc3Ryb25nbHkgY29ycmVsYXRlZCB3aXRoIHRvdGFsX2JlZHJvb21zLiBVc2UgZ2dwYWlycygpIHRvIGludmVzdGlnYXRlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHRoZXNlIHR3byB2YXJpYWJsZXMuCgpgYGB7cn0KaG91c2luZyAlPiUgCiAgc2VsZWN0KHRvdGFsX3Jvb21zLCB0b3RhbF9iZWRyb29tcykgJT4lIAogIGdncGFpcnMocHJvZ3Jlc3MgPSBGQUxTRSkKCmBgYAoKIyAzLiAKU28sIHdlIGRvIGZpbmQgc2lnbmlmaWNhbnQgY29ycmVsYXRpb25zLiBMZXTigJlzIGRyb3AgdG90YWxfYmVkcm9vbXMgZnJvbSB0aGUgZGF0YXNldCwgYW5kIHVzZSBvbmx5IHRvdGFsX3Jvb21zIGdvaW5nIGZvcndhcmQuCgpgYGB7cn0KaG91c2luZ190cmltIDwtIGhvdXNpbmcgJT4lIAogIHNlbGVjdCgtdG90YWxfYmVkcm9vbXMpCgpob3VzaW5nX3ByaWNlc190cmltIDwtIGhvdXNpbmdfcHJpY2VzICU+JSAKICBzZWxlY3QoLXRvdGFsX2JlZHJvb21zKQpgYGAKCiMjIyMgQWRkZWQgQXQgUmV2aWV3IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpXZSBjb3VsZCBlbmdpbmVlciBzb21lIG5ldyB2YXJpYWJsZXMuCgpgYGB7cn0KaG91c2luZ190cmltX2VuZyA8LSBob3VzaW5nX3RyaW0gJT4lCiAgbXV0YXRlKHJvb21zX3Blcl9ob3VzZSA9IHRvdGFsX3Jvb21zIC8gaG91c2Vob2xkcywKICAgICAgICAgcGVvcGxlX3Blcl9ob3VzZSA9IHBvcHVsYXRpb24gLyBob3VzZWhvbGRzLAogICAgICAgICByb29tc19wZXJfcGVyc29uID0gdG90YWxfcm9vbXMgLyBwb3B1bGF0aW9uKQoKaG91c2luZ190cmltX2VuZwoKaG91c2luZ19wcmljZXNfZW5nIDwtIGhvdXNpbmdfcHJpY2VzX3RyaW0gJT4lCiAgbXV0YXRlKHJvb21zX3Blcl9ob3VzZSA9IHRvdGFsX3Jvb21zIC8gaG91c2Vob2xkcywKICAgICAgICAgcGVvcGxlX3Blcl9ob3VzZSA9IHBvcHVsYXRpb24gLyBob3VzZWhvbGRzLAogICAgICAgICByb29tc19wZXJfcGVyc29uID0gdG90YWxfcm9vbXMgLyBwb3B1bGF0aW9uKQoKaG91c2luZ19wcmljZXNfZW5nCgoKYGBgCgpBbmQgdGFrZSBhIGxvb2sgYXQgdGhlc2UgdXNpbmcgYHNraW0oKWAgLSB3aGljaCB3aGVuIHdlIGRvLCB3ZSBjYW4gc2VlIHRoYXQgYSBsb3Qgb2YgdGhlIHZhcmlhYmxlcyBhcmUgcG9zaXRpdmVseSBza2V3ZWQuCgpQb3NpdGl2ZWx5IHNrZXdlZCBkYXRhIGlzIG5vdCBnb29kIGZvciBsaW5lYXIgbW9kZWxzLgoKYGBge3J9Cgpob3VzaW5nX3RyaW1fZW5nICU+JSAKICBza2ltcjo6c2tpbSgpICU+JSAKICB2aWV3KCkKCmBgYAoKV2hlcmUgd2UgaGF2ZSBza2V3ZWQgZGF0YSB0aGF0IGlzIG5vbi1uZWdhdGl2ZSwgd2UgY2FuIGxvZyB0cmFuc2Zvcm0gaXQgdG8gbWFrZSBpdCBtb3JlIG5vcm1hbC4KCmBgYHtyfQoKaG91c2luZ19wcmljZXNfZW5nICU+JQogIGdncGxvdChhZXMoeCA9IG1lZGlhbl9pbmNvbWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKQoKaG91c2luZ19wcmljZXNfZW5nICU+JQogIGdncGxvdChhZXMoeCA9IG1lZGlhbl9ob3VzZV92YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbSgpCgpgYGAKCmBgYHtyfQpob3VzaW5nX2xvZyA8LSBob3VzaW5nX3ByaWNlc19lbmcgJT4lIAogIHNlbGVjdCgtYyhvY2Vhbl9wcm94aW1pdHksIGxhdGl0dWRlLCBsb25naXR1ZGUsIGhvdXNpbmdfbWVkaWFuX2FnZSkpICU+JSAKICBtdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgbG9nKSkgJT4lIAogIHJlbmFtZV93aXRoKH4gcGFzdGUwKCJsb2dfIiwgLngpKSAlPiUgCiAgYmluZF9jb2xzKGhvdXNpbmdfcHJpY2VzX2VuZykKYGBgCgpgYGB7cn0KaG91c2luZ19sb2cgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9nX21lZGlhbl9pbmNvbWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKQoKaG91c2luZ19sb2cgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9nX21lZGlhbl9ob3VzZV92YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKV2l0aCB2YWx1ZXMgbm93IHRyYW5zZm9ybWVkIGFuZCBtb3JlIG5vcm1hbC4gTGV0cyBoYXZlIGEgbG9vayBmb3Igc29tZSByZWxhdGlvbnNoaXBzLgoKYGBge3J9CmhvdXNpbmdfbG9nICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBtZWRpYW5faW5jb21lLCB5ID0gbWVkaWFuX2hvdXNlX3ZhbHVlKSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyfQoKaG91c2luZ19sb2cgJT4lIAogIGdncGxvdChhZXMoeCA9IG9jZWFuX3Byb3hpbWl0eSwgeSA9IG1lZGlhbl9ob3VzZV92YWx1ZSkpICsKICBnZW9tX2JveHBsb3QoKQoKYGBgCgpUaGVyZSBhcHBlYXJzIHRvIGJlIHNvbWUga2luZCBvZiByZWxhdGlvbnNoaXAgYmV0d2VlbiBvY2VhbiBwcm94aW1pdHkgYW5kIGhvdXNlIHZhbHVlLgoKYGBge3J9CmhvdXNpbmdfbG9nICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBsb25naXR1ZGUsIHkgPSBsYXRpdHVkZSwgY29sb3VyID0gb2NlYW5fcHJveGltaXR5KSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCkxldHMgZ3JvdXAgc29tZSBvZiB0aG9zZSBsZXZlbHMhCgpgYGB7cn0KaG91c2luZ19vY2VhbiA8LSBob3VzaW5nX2xvZyAlPiUgCiAgbXV0YXRlKG9jZWFuX3Byb3hfZ3JvdXBlZCA9IGlmX2Vsc2UoCiAgICBvY2Vhbl9wcm94aW1pdHkgJWluJSBjKCI8MUggT0NFQU4iLCAiTkVBUiBCQVkiLCAiTkVBUiBPQ0VBTiIpLCAiTkVBUiBXQVRFUiIsIG9jZWFuX3Byb3hpbWl0eSkpCgpob3VzaW5nX29jZWFuCmBgYAoKYGBge3J9CmhvdXNpbmdfb2NlYW4gJT4lIAogIGdncGxvdChhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBjb2xvdXIgPSBvY2Vhbl9wcm94X2dyb3VwZWQpKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9Cgpob3VzaW5nX29jZWFuICU+JQogIHNlbGVjdChsb2dfbWVkaWFuX2hvdXNlX3ZhbHVlLCAKICAgICAgICAgbG9nX3RvdGFsX3Jvb21zLCAKICAgICAgICAgbG9nX3BvcHVsYXRpb24sIAogICAgICAgICBsb2dfaG91c2Vob2xkcywKICAgICAgICAgbG9nX3Jvb21zX3Blcl9ob3VzZSwKICAgICAgICAgbG9nX3Blb3BsZV9wZXJfaG91c2UsCiAgICAgICAgIGxvZ19yb29tc19wZXJfcGVyc29uKSAlPiUgCiAgZ2dwYWlycyhwcm9ncmVzcyA9IEZBTFNFKQoKYGBgCgpgYGB7cn0KaG91c2luZ19vY2VhbiAlPiUKICBzZWxlY3QobG9nX21lZGlhbl9ob3VzZV92YWx1ZSwgaG91c2luZ19tZWRpYW5fYWdlLCBsb2dfbWVkaWFuX2luY29tZSwgb2NlYW5fcHJveF9ncm91cGVkKSAlPiUgCiAgZ2dwYWlycyhhZXMoY29sb3VyID0gb2NlYW5fcHJveF9ncm91cGVkLCAKICAgICAgICAgICAgICBhbHBoYSA9IDAuNSksCiAgICAgICAgICBwcm9ncmVzcyA9IEZBTFNFKQpgYGAKCmBgYHtyfQptb2RlbDEgPC0gbG0obG9nX21lZGlhbl9ob3VzZV92YWx1ZSB+IGxvZ19tZWRpYW5faW5jb21lLAogICAgICAgICAgICAgZGF0YSA9IGhvdXNpbmdfb2NlYW4pCgphdXRvcGxvdChtb2RlbDEpCmBgYAoKYGBge3J9CnN1bW1hcnkobW9kZWwxKQpgYGAKCgpPbmNlIGhhdmUgYSBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24gd2Ugd2FudCB0byBpZGVudGlmeSB3aGljaCBwcmVkaWN0b3JzIHRvIGFkZCBuZXh0LgoKV2UgZG8gdGhpcyBieSBjb25zaWRlcmluZyBvdXIgcmVzaWR1YWxzLgoKYGBge3J9CmxpYnJhcnkobW9kZWxyKQpgYGAKCmBgYHtyfQpob3VzaW5nX29jZWFuICU+JSAKICBhZGRfcmVzaWR1YWxzKG1vZGVsID0gbW9kZWwxKSAlPiUgICMgQWRkcyByZWRpcyBjb2x1bW4sIGRpZmZlcmVuY2UgZnJvbSBhY3R1YWwgdG8gZml0dGVkIG1lZGlhbl9ob3VzZV92YWx1ZSAKICBzZWxlY3QobG9nX21lZGlhbl9ob3VzZV92YWx1ZSwgCiAgICAgICAgIG9jZWFuX3Byb3hfZ3JvdXBlZCwgCiAgICAgICAgIGxvZ19yb29tc19wZXJfcGVyc29uLCAKICAgICAgICAgbG9nX3Blb3BsZV9wZXJfaG91c2UsIAogICAgICAgICByZXNpZCkgJT4lIAogIGdncGFpcnMocHJvZ3Jlc3MgPSBGQUxTRSkKYGBgCgoKYGBge3J9Cm1vZGVsMSA8LSBsbShsb2dfbWVkaWFuX2hvdXNlX3ZhbHVlIH4gbG9nX21lZGlhbl9pbmNvbWUsCiAgICAgICAgICAgICBkYXRhID0gaG91c2luZ19vY2VhbikKCmF1dG9wbG90KG1vZGVsMSkKYGBgCgojIDQuMQoKV2UgYXJlIGludGVyZXN0ZWQgaW4gZGV2ZWxvcGluZyBhIHJlZ3Jlc3Npb24gbW9kZWwgZm9yIHRoZSBtZWRpYW5faG91c2VfdmFsdWUgb2YgYSBob3VzZSBpbiB0ZXJtcyBvZiB0aGUgcG9zc2libGUgcHJlZGljdG9yIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldC4KClVzZSBnZ3BhaXJzKCkgdG8gaW52ZXN0aWdhdGUgY29ycmVsYXRpb25zIGJldHdlZW4gbWVkaWFuX2hvdXNlX3ZhbHVlIGFuZCB0aGUgcHJlZGljdG9ycyAodGhpcyBtYXkgdGFrZSBhIHdoaWxlIHRvIHJ1biwgZG9u4oCZdCB3b3JyeSwgbWFrZSBjb2ZmZWUgb3Igc29tZXRoaW5nKS4KCmBgYHtyfQpnZ3BhaXJzKGhvdXNpbmdfdHJpbSwgcHJvZ3Jlc3MgPSBGQUxTRSkKYGBgCgojIDQuMgpQZXJmb3JtIGZ1cnRoZXIgZ2dwbG90IHZpc3VhbGlzYXRpb25zIG9mIGFueSBzaWduaWZpY2FudCBjb3JyZWxhdGlvbnMgeW91IGZpbmQuCgpgZ2dwYWlycygpYCBzaG93cyBhIHN0cm9uZyBjb3JyZWxhdGlvbiBiZXR3ZWVuIGBtZWRpYW5faG91c2VfdmFsdWVgIGFuZCBgbWVkaWFuX2luY29tZWAsIHdpdGggYSBDb3JyZWxhdGlvbiBDb2VmZmljaWVudCBvZiAwLjY4OC4gCgpUaGlzIGlzIHZpc3VhbGlzZWQgYmVsb3cuCgpgYGB7cn0KCmhvdXNpbmdfdHJpbSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbWVkaWFuX2luY29tZSwgeSA9IG1lZGlhbl9ob3VzZV92YWx1ZSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpCgpgYGAKCiMgNS4gClNob3J0bHkgd2UgbWF5IHRyeSBhIHJlZ3Jlc3Npb24gbW9kZWwgdG8gZml0IHRoZSBjYXRlZ29yaWNhbCBwcmVkaWN0b3Igb2NlYW5fcHJveGltaXR5LiBJbnZlc3RpZ2F0ZSB0aGUgbGV2ZWwgb2Ygb2NlYW5fcHJveGltaXR5IHByZWRpY3RvcnMuIEhvdyBtYW55IGR1bW15IHZhcmlhYmxlcyBkbyB5b3UgZXhwZWN0IHRvIGdldCBmcm9tIGl0PwoKVGhlcmUgYXJlIDUgbGV2ZWxzIG9mIGBvY2Vhbl9wcm94aW1pdHlgIHNvIHdlIHdvdWxkIHVzZSA0IGR1bW1pZXMuIAoKYGBge3J9Cgpob3VzaW5nX3RyaW0gJT4lIAogIGRpc3RpbmN0KG9jZWFuX3Byb3hpbWl0eSkKYGBgCgojIDYuClN0YXJ0IHdpdGggc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uLiBSZWdyZXNzIG1lZGlhbl9ob3VzZV92YWx1ZSBvbiBtZWRpYW5faW5jb21lIGFuZCBjaGVjayB0aGUgcmVncmVzc2lvbiBkaWFnbm9zdGljcy4KCmBgYHtyfQpob3VzaW5nX21vZGVsIDwtIGxtKG1lZGlhbl9ob3VzZV92YWx1ZSB+IG1lZGlhbl9pbmNvbWUsIAogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBob3VzaW5nX3RyaW0pCmBgYAoKYGBge3J9CmF1dG9wbG90KGhvdXNpbmdfbW9kZWwpCmBgYAoKUGxvdCAxOiBJJ20gdW5zdXJlIG9mIHRoZSBjb3JyZWN0IGludGVycHJldGF0aW9uIG9mIHRoaXM7IGhvd2V2ZXIsIHRoZSBzbW9vdGhlZCBsaW5lIHN1Z2dlc3RzIG5vIGNsZWFyIHBhdHRlcm4gdG8gZGlzdHJpYnV0aW9uIHN0YXlpbmcgcmVsYXRpdmVseSBmbGF0IGFuZCBjbG9zZSB0byAwLiBUbyB0aGUgbXkgZXllIHRob3VnaCBpdCBsb29rcyBhcyB0aG91Z2ggbG93ZXIgZml0dGVkIHZhbHVlcyBhcmUgb3Zlci1lc3RpbWF0ZWQgYW5kIGhpZ2hlciBmaXR0ZWQgdmFsdWVzIGFyZSB1bmRlci1lc3RpbWF0ZWQuIApQbG90IDI6IFRoZXJlIGlzIHNvbWUgdmFyaWF0aW9uIGZyb20gdGhlIG5vcm1hbCBkaXN0cmlidXRpb24sIGFsdGhvdWdoIHRoZSByZXNpZHVhbHMgYXJlIHJlbGF0aXZlbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuClBsb3QgMzogSSdtIHVuc3VyZSBvbiB0aGUgY29ycmVjdCBpbnRlcnByZXRhdGlvbiBvZiB0aGlzOyBob3dldmVyLCB0aGUgc21vb3RoZWQgbGluZSBzdGF5cyByZWxhdGl2ZWx5IGNvbnN0YW50L2ZsYXQgYWx0aG91Z2ggdG8gbXkgZXllIHRoZXJlIGxvb2tzIHRvIGJlIGZ1bm5lbGluZyBhdCB0aGUgaGlnaGVyIGVuZCBvZiB0aGUgZml0dGVkIHZhbHVlcy4gCgojIDcKQWRkIGFub3RoZXIgcHJlZGljdG9yIG9mIHlvdXIgY2hvaWNlLiBDaGVjayB5b3VyIGFzc3VtcHRpb25zLCBkaWFnbm9zdGljcywgYW5kIGludGVycHJldCB0aGUgbW9kZWwuCgpgYGB7cn0KaG91c2luZ190cmltICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBtZWRpYW5faG91c2VfdmFsdWUsIHkgPSBvY2Vhbl9wcm94aW1pdHkpKSsKICBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyfQpob3VzaW5nX21vZGVsXzIgPC0gbG0obWVkaWFuX2hvdXNlX3ZhbHVlIH4gbWVkaWFuX2luY29tZSArIG9jZWFuX3Byb3hpbWl0eSwgCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGhvdXNpbmdfdHJpbSkKYGBgCgpgYGB7cn0KYXV0b3Bsb3QoaG91c2luZ19tb2RlbF8yKQpgYGAKCk15IGludGVycHJldGF0aW9ucyBoZXJlIGFyZSB0aGUgc2FtZSBhcyB0aGUgZmlyc3QgbW9kZWwsIGFsdGhvdWdoIGFnYWluIEknbSBhIGxpdHRsZSB1bnN1cmUgaWYgdGhlc2UgYXJlIGNvcnJlY3QuCgpQbG90IDE6IEknbSB1bnN1cmUgb2YgdGhlIGNvcnJlY3QgaW50ZXJwcmV0YXRpb24gb2YgdGhpczsgaG93ZXZlciwgdGhlIHNtb290aGVkIGxpbmUgc3VnZ2VzdHMgbm8gY2xlYXIgcGF0dGVybiB0byBkaXN0cmlidXRpb24gc3RheWluZyByZWxhdGl2ZWx5IGZsYXQgYW5kIGNsb3NlIHRvIDAuIFRvIHRoZSBteSBleWUgdGhvdWdoIGl0IGxvb2tzIGFzIHRob3VnaCBsb3dlciBmaXR0ZWQgdmFsdWVzIGFyZSBvdmVyLWVzdGltYXRlZCBhbmQgaGlnaGVyIGZpdHRlZCB2YWx1ZXMgYXJlIHVuZGVyLWVzdGltYXRlZC4gClBsb3QgMjogVGhlcmUgaXMgc29tZSB2YXJpYXRpb24gZnJvbSB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgYWx0aG91Z2ggdGhlIHJlc2lkdWFscyBhcmUgcmVsYXRpdmVseSBub3JtYWxseSBkaXN0cmlidXRlZC4KUGxvdCAzOiBJJ20gdW5zdXJlIG9uIHRoZSBjb3JyZWN0IGludGVycHJldGF0aW9uIG9mIHRoaXM7IGhvd2V2ZXIsIHRoZSBzbW9vdGhlZCBsaW5lIHN0YXlzIHJlbGF0aXZlbHkgY29uc3RhbnQvZmxhdCBhbHRob3VnaCB0byBteSBleWUgdGhlcmUgbG9va3MgdG8gYmUgZnVubmVsaW5nIGF0IHRoZSBoaWdoZXIgZW5kIG9mIHRoZSBmaXR0ZWQgdmFsdWVzLiAKCmBgYHtyfQpzdW1tYXJ5KGhvdXNpbmdfbW9kZWxfMikKYGBgCgotIFRoZSBBZGp1c3RlZCBSLXNxdWFyZWQgdmFsdWUgaXMgMC41ODc5IG1lYW5pbmcgdGhhdCB0b2dldGhlciB0aGUgcHJlZGljdG9ycyBvZiBtZWRpYW5faW5jb21lIGFuZCBvY2Vhbl9wcm94aW1pdHkgZXhwbGFpbiA2OSUgb2YgbWVkaWFuX2hvdXNlX3ZhbHVlIHZhcmlhdGlvbi4KCi0gVGhlIHAtdmFsdWVzIGZvciBlYWNoIG9mIHRoZSBpbmRpdmlkdWFsIHByZWRpY3RvcnMgYXJlIHZlcnkgbG93LCBzdWdnZXN0aW5nIHRoYXQgYWxsIHByZWRpY3RvcnMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4KCgo=